/* btree.c */
#include "global_macros.h"
#include "btree.h"


/* safety only definition in case we have the wrong header cause of typo */
extern void btree_print(BTREE *tree);

/**
 * Print the tree recursivly.
 * @param node - The node from which to print.
 * @param offset - The current offset from the root.
 * @param print_function - A function used to print the data inside the node.
 */
static void btree_print_rec(BTREE_NODE *node, int offset,
                            PRINT_FCT print_function);

extern BTREE *btree_new(unsigned char data,
                        DESTROY_FCT destroy_data, PRINT_FCT print_data, 
                        int value)
{
    BTREE *tree = (BTREE *) malloc(sizeof (BTREE));
    ENSURE_ENOUGH_MEMORY(tree, "btree_new");

    tree->root = btreenode_new(data);
    tree->destroy_data = destroy_data;
    tree->print_data = print_data;
    tree->value = value;
    
    return tree;
}

extern BTREE *btree_clone(BTREE *tree)
{
    BTREE *cloned_tree = btree_new(tree->root->value, NULL, NULL, tree->value);

    cloned_tree->root = btreenode_clone(tree->root);
    cloned_tree->destroy_data = tree->destroy_data;
    cloned_tree->print_data = tree->print_data;

    return cloned_tree;
}

extern void btree_destroy(BTREE *tree, BOOL destroy_data)
{
    DESTROY_FCT destroy_function;
    if (destroy_data)
    {
        destroy_function = tree->destroy_data;
    }
    else
    {
        destroy_function = NULL;
    }
    free(tree);
}

extern BOOL btree_equals(BTREE *tree1, BTREE *tree2)
{
    return btreenode_equals(tree1->root, tree2->root);
}

extern BTREE_NODE *btree_get_root(BTREE *tree)
{
    if (tree)
    {
        return tree->root;
    }
    else
    {
        return NULL;
    }
}

extern BTREE *btree_merge(BTREE *left, BTREE *right, char data)
{
    BTREE *merged_tree = btree_new(data, left->destroy_data, left->print_data,
                                   left->value + right->value);
    btreenode_set_left(merged_tree->root,left->root);
    btreenode_set_right(merged_tree->root, right->root);

    return merged_tree;
}

extern void btree_print(BTREE *tree)
{
    btree_print_rec(tree->root, 0, tree->print_data);
}

static void btree_print_rec(BTREE_NODE *node, int offset,
                            PRINT_FCT print_function)
{
    int i;
    if(!node) {
        return;
    }

    for (i = 0; i < offset; ++i)
    {
        printf("  ");
    }
    /* print element itself */
    if(print_function) {
        printf("|-- (0x%x:", node);
        printf(")");
    }
    else {
        printf("|-- 0x%x", node);
    }
    printf("\n");
    /* print children */
    btree_print_rec(node->left_child, offset + 1, print_function);
    btree_print_rec(node->right_child, offset + 1, print_function);
}
